// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package modload

// 该文件包含模块模式的包加载器，以及一些与包导入图有关的附件
// 函数。
// 
// 有两个导出的包加载入口点——LoadPackages和
// ImportFromFiles——都是根据loadFromRoots实现的，loadFromRoots本身
// 操作加载器结构的实例。
// 
// 虽然大多数加载状态都保存在加载器结构中，但
// 其中一个关键部分——构建列表——是全局的，因此可以对其进行修改
// 与加载操作分离，例如在“go get”
// 升级/降级或“go mod”操作期间。
// TODO（#40775）：让加载器获取并返回一个构建列表，而不是使用全局函数进行硬编码，这可能会很好。
// 
// 加载是一个迭代过程。在每次迭代中，我们尝试加载
// 请求的包及其可传递的导入，然后尝试解析模块
// 中仍然缺少的任何导入包。
// 
// 每个迭代的第一步都标识一组“根”包。
// 通常根包与命名模式
// 参数完全匹配。然而，对于“all”元模式，最后一组包是根据包导入图计算的
// 因此不能是加载该图的初始
// 输入。相反，“all”模式
// 的根包是主模块中包含的根包，
// 加载程序的allPatternIsRoot参数指示它在加载过程中动态地将这些根扩展到完整的“all”
// 模式。
// 
// 每个loadPkg实例上的pkgInAll标志跟踪该
// 包是否已知与“all”元模式匹配。
// 如果一个包与“all”模式匹配：
// -它在主模块中，或
// -它通过主模块中的任何测试导入，或
// -它由“all”中的另一个包导入，或
// -主模块指定go版本≤ 1.15，该包装为进口
// 。
// 
// 当图形修剪生效时，我们想抽查图形修剪
// 不变量——这取决于已知哪些包位于“all”中——甚至
// 当我们只加载单个包时，所以我们设置pkgInAll标志
// 无论“all”模式是否是根。
// （这是维护
// 中描述的“导入不变量”所必需的）https:
// 
// 因为“go mod vendor”删减了对供应商软件包的测试，所以go 1.11–1.15中带有-mod=vendor的“all”模式的行为与1.16+中的“all”模式（无论-mod标志如何）相同。
// 加载器使用GoVersion参数来确定“all”
// 模式是否应该结束测试（如Go 1.11–1.15所示），或者只停止在主模块
// 包。
// /（“Go 1.16+中的all”和Go 1.11+中的“Go mod vendor”中通过包和测试传递导入的那些
// 
// 请注意，即使我们
// 正在加载“all”模式，加载的包也可能不在“all”模式中。例如，必须加载可传递的
// 依赖于命令行上命名的其他根的包，但
// 不在“all”中。（mod_notall测试说明了这种行为。）
// 类似地，如果设置了LoadTests标志，但“all”模式没有关闭
// 覆盖测试依赖项，那么当我们加载位于
// “all”但在主模块之外的包的测试时，该测试的依赖项本身不一定位于“all”中。（这种配置在Go 
// 1.11–1.15中不会出现，但在Go 1.16+中是可能的。）
// 
// 从根开始加载，使用对
// 活动工作量有限制的并行工作队列（以避免磁盘、CPU内核和/或
// 网络连接饱和）。每个包第一次被
// 由另一个包导入。当我们完成
// 一个包的进口识别后，如果需要，我们会为该包添加测试。如果以下情况，则测试可能是
// /，需要：
// /-包匹配根模式并请求根的测试，或
// /-包位于主模块中，请求“all”模式
// /（因为“all”模式包括主
// /）模块中测试的依赖项），或者
// -包在“all”中，我们使用的“all”的定义包括测试的依赖性（如Go中的情况）≤1.15). 
// 
// 加载所有可用的包后，我们检查结果，以
// 确定任何仍缺失的请求或导入的包，如果
// 如此，我们可以向模块图中添加哪些模块，以使
// 缺失的包可用。我们将其添加到模块图中并迭代，直到所有包成功解析，或者我们无法识别任何将解析任何剩余缺失包的模块。
// 
// 如果主模块是“整洁的”（也就是说，如果“go mod tidy”对它来说是不可操作的）
// 并且所有请求的包都在“all”中，那么加载在单个
// 迭代中完成。
// TODO（bcmills）：如果
// 请求的包都来自自身整洁的模块，那么我们也应该能够在一次迭代中加载，而不管
// 这些包是否在“all”中。今天，如果在主模块的现有依赖项中找不到这些包，则需要两次迭代。

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"go/build"
	"io/fs"
	"os"
	"path"
	pathpkg "path"
	"path/filepath"
	"reflect"
	"runtime"
	"sort"
	"strings"
	"sync"
	"sync/atomic"

	"cmd/go/internal/base"
	"cmd/go/internal/cfg"
	"cmd/go/internal/fsys"
	"cmd/go/internal/imports"
	"cmd/go/internal/modfetch"
	"cmd/go/internal/mvs"
	"cmd/go/internal/par"
	"cmd/go/internal/search"
	"cmd/go/internal/str"

	"golang.org/x/mod/module"
	"golang.org/x/mod/semver"
)

// loaded是最近使用的包加载器。
// 它包含有关单个包的详细信息。
// 
// 此变量只能在顶级导出的
// 函数中直接访问。所有其他需要或产生*loader的函数都应该传递
// 或将其作为显式参数返回。
var loaded *loader

// packagepts控制LoadPackages函数的行为。
type PackageOpts struct {
	// GoVersion是Go的Go版本。mod文件应在加载包后更新
	// 。
	// 
	// 空GoVersion表示使用主模块Go中已经指定的Go版本。mod文件，如果没有主
	// 模块，则为最新Go版本。
	GoVersion string

	// 标记是有效的构建标记（由
	// cmd/go/internal/imports包解释）。
	// 如果为零，则视为等同于进口。标签（）。
	Tags map[string]bool

	// Tidy，如果为true，则请求生成列表并启动。sum file应缩减为
	// 重新加载请求的
	// 包所需的最小依赖项。
	Tidy bool

	// TidyCompatibleVersion是最早的Go版本，必须能够重复地重新加载请求的包。
	// 
	// 如果为空，则兼容版本为Go中列出的
	// /'Go'版本之前的Go版本。mod文件。
	TidyCompatibleVersion string

	// VendorModulesInGOROOTSrc表示，如果我们在
	// GOROOT/src中的一个模块内，则该模块的供应商目录中的包应解析为
	// 实际的模块依赖项（而不是标准库包）。
	VendorModulesInGOROOTSrc bool

	// ResolveMissingImports表示我们应该根据需要添加模块
	// 依赖项，以解析未找到的包的导入。
	// 
	// 对于支持-mod标志的命令，如果该标志设置为“只读”（默认）或“供应商”，解析导入仍可能失败。
	ResolveMissingImports bool

	// AssumeRootsPorted表示根
	// 包的可传递依赖项应被视为这些根将由主
	// 模块导入。
	AssumeRootsImported bool

	// AllowPackage，如果非nil，则在识别提供
	// 每个包的模块后调用。如果AllowPackage返回非nil错误，则该错误被设置为
	// 用于包，并且该包的导入和测试将不会被
	// 加载。
	// 
	// AllowPackage可以由多个Goroutine同时调用，
	// 并且可以为给定的包路径多次调用。
	AllowPackage func(ctx context.Context, path string, mod module.Version) error

	// LoadTests加载与请求的
	// 图案。如果ResolveMissingImports也为true，则测试依赖项将为
	// 如果缺少，则已解决。
	LoadTests bool

	// UseVendorAll导致“all”包模式被解释为运行“go mod vendor”（或使用“-mod=vendor”构建）。
	// 
	// 对于声明为“go 1.16”或更高版本的模块，这是不可操作的，对于这些模块，此
	// 是模块模式中“all”模式的默认（也是唯一）解释。
	UseVendorAll bool

	// AllowErrors表示如果发生错误，LoadPackages不应终止进程。
	AllowErrors bool

	// 沉默包错误表示LoadPackages不应打印匹配或加载包时出现的错误
	// 并且在出现此类错误时不应终止
	// 进程。
	// 
	// 仍将报告模块图中遇到的错误。
	// 
	// 调用者可以使用Lookup 
	// 函数检索被沉默的包错误，并且匹配的错误仍然填充在
	// 关联搜索的Errs字段中。匹配。）
	SilencePackageErrors bool

	// 沉默发射stdimports表示如果导入的包丢失，LoadPackages不应打印
	// 错误或终止进程，并且
	// 导入路径看起来可能在标准库中（可能在
	// 未来版本中）。
	SilenceMissingStdImports bool

	// 沉默浏览错误表示加载包不应打印
	// 导入。错误。
	// 这允许调用方调用LoadPackages（并报告其他错误）
	// 而不知道给定标记是否存在请求的包。
	// 
	// 请注意，如果请求的包根本不存在*，它将在模块解析期间失败，并且错误不会被抑制。
	SilenceNoGoErrors bool

	// 沉默匹配抑制通常为
	// 不匹配任何包的模式发出的警告。
	SilenceUnmatchedWarnings bool

	// 解析针对此模块的查询。
	MainModule module.Version
}

// LoadPackages标识与给定模式匹配的包集，
// 在以该集为根的导入图中加载包。
func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (matches []*search.Match, loadedPackages []string) {
	if opts.Tags == nil {
		opts.Tags = imports.Tags()
	}

	patterns = search.CleanPatterns(patterns)
	matches = make([]*search.Match, 0, len(patterns))
	allPatternIsRoot := false
	for _, pattern := range patterns {
		matches = append(matches, search.NewMatch(pattern))
		if pattern == "all" {
			allPatternIsRoot = true
		}
	}

	updateMatches := func(rs *Requirements, ld *loader) {
		for _, m := range matches {
			switch {
			case m.IsLocal():
				// 在第一次迭代时评估文件系统目录列表。
				if m.Dirs == nil {
					matchModRoots := modRoots
					if opts.MainModule != (module.Version{}) {
						matchModRoots = []string{MainModules.ModRoot(opts.MainModule)}
					}
					matchLocalDirs(ctx, matchModRoots, m, rs)
				}

				// 复制目录列表并转换为导入路径。
				// 请注意，目录是否对应于导入路径
				// 随着构建列表的更新而更改，并且目录可以将
				// 从不在构建列表中更改为在构建列表中，然后作为
				// 在
				// 加载程序迭代期间，特定模块的确切版本会增加。
				m.Pkgs = m.Pkgs[:0]
				for _, dir := range m.Dirs {
					pkg, err := resolveLocalPackage(ctx, dir, rs)
					if err != nil {
						if !m.IsLiteral() && (err == errPkgIsBuiltin || err == errPkgIsGorootSrc) {
							continue // 不要在通配符模式中包含“builtin”或GOROOT/src。
						}

						// 如果我们在模块之外，请确保故障模式
						// 表明了这一点。
						if !HasModRoot() {
							die()
						}

						if ld != nil {
							m.AddError(err)
						}
						continue
					}
					m.Pkgs = append(m.Pkgs, pkg)
				}

			case m.IsLiteral():
				m.Pkgs = []string{m.Pattern()}

			case strings.Contains(m.Pattern(), "..."):
				m.Errs = m.Errs[:0]
				mg, err := rs.Graph(ctx)
				if err != nil {
					// 模块图不完整（或可能不完整）——也许我们未能加载某些模块的需求。这是将模式与包匹配时出现的错误，因为我们可能缺少一些包
					// 或者我们可能错误地匹配了错误版本的
					// 模块中的包。然而，对于“go list-e”这样的情况，这个错误不应该阻止我们加载我们可以找到的包。
					m.Errs = append(m.Errs, err)
				}
				matchPackages(ctx, m, opts.Tags, includeStd, mg.BuildList())

			case m.Pattern() == "all":
				if ld == nil {
					// 初始根是主模块中的包。
					// loadFromRoots将其扩展为“所有”。
					m.Errs = m.Errs[:0]
					matchModules := MainModules.Versions()
					if opts.MainModule != (module.Version{}) {
						matchModules = []module.Version{opts.MainModule}
					}
					matchPackages(ctx, m, opts.Tags, omitStd, matchModules)
				} else {
					// 从主模块中的包开始，
					// 列举“所有”的完整列表。
					m.Pkgs = ld.computePatternAll()
				}

			case m.Pattern() == "std" || m.Pattern() == "cmd":
				if m.Pkgs == nil {
					m.MatchPackages() // 在GOROOT/src中查找包。
				}

			default:
				panic(fmt.Sprintf("internal error: modload missing case for pattern %s", m.Pattern()))
			}
		}
	}

	initialRS := LoadModFile(ctx)

	ld := loadFromRoots(ctx, loaderParams{
		PackageOpts:  opts,
		requirements: initialRS,

		allPatternIsRoot: allPatternIsRoot,

		listRoots: func(rs *Requirements) (roots []string) {
			updateMatches(rs, nil)
			for _, m := range matches {
				roots = append(roots, m.Pkgs...)
			}
			return roots
		},
	})

	// 最后一次完成通配符。
	updateMatches(ld.requirements, ld)

	// 列出匹配模式中的错误（例如目录权限
	// 通配符模式中的错误）。
	if !ld.SilencePackageErrors {
		for _, match := range matches {
			for _, err := range match.Errs {
				ld.errorf("%v\n", err)
			}
		}
	}
	base.ExitIfErrors()

	if !opts.SilenceUnmatchedWarnings {
		search.WarnUnmatched(matches)
	}

	if opts.Tidy {
		if cfg.BuildV {
			mg, _ := ld.requirements.Graph(ctx)

			for _, m := range initialRS.rootModules {
				var unused bool
				if ld.requirements.pruning == unpruned {
					// m如果完全从模块图中删除，则它未被使用。如果
					// 仅从直接降级为间接，则它可能仍在通过
					// 过渡导入使用。
					unused = mg.Selected(m.Path) == "none"
				} else {
					// m如果是从根上掉下来的，就不用了。如果它仍然作为可传递依赖项存在，则主模块中的任何包或测试都不需要该可传递依赖项。
					_, ok := ld.requirements.rootSelected(m.Path)
					unused = !ok
				}
				if unused {
					fmt.Fprintf(os.Stderr, "unused %s\n", m.Path)
				}
			}
		}

		keep := keepSums(ctx, ld, ld.requirements, loadedZipSumsOnly)
		if compatDepth := pruningForGoVersion(ld.TidyCompatibleVersion); compatDepth != ld.requirements.pruning {
			compatRS := newRequirements(compatDepth, ld.requirements.rootModules, ld.requirements.direct)
			ld.checkTidyCompatibility(ctx, compatRS)

			for m := range keepSums(ctx, ld, compatRS, loadedZipSumsOnly) {
				keep[m] = true
			}
		}

		if !ExplicitWriteGoMod {
			modfetch.TrimGoSum(keep)

			// 下面的committerequirements也将调用WriteGoSum，但这里的“keep”映射可能会更大：committerequirements只提交
			// 加载。需求，但在这里我们可能也加载了（并且想要
			// 为来自Comparts的额外实体保留校验和，这些实体是
			// 仅用于与ld兼容。整洁的电脑版。
			if err := modfetch.WriteGoSum(keep, mustHaveCompleteRequirements()); err != nil {
				base.Fatalf("go: %v", err)
			}
		}

		// 更新围棋。如有必要，修改文件的Go版本。
		modFile := MainModules.ModFile(MainModules.mustGetSingleMainModule())
		if ld.GoVersion != "" {
			modFile.AddGoStmt(ld.GoVersion)
		}
	}

	// 成功！更新开始。mod和go。求和（如果需要）并返回结果。
	// 如果ExplicitWriteGoMod为true（调用者选择了
	// 调用WriteGoMod本身），或者如果ResolveMissingImports为false（
	// 命令希望按原样检查包图），我们将跳过更新。
	loaded = ld
	requirements = loaded.requirements

	for _, pkg := range ld.pkgs {
		if !pkg.isTest() {
			loadedPackages = append(loadedPackages, pkg.path)
		}
	}
	sort.Strings(loadedPackages)

	if !ExplicitWriteGoMod && opts.ResolveMissingImports {
		if err := commitRequirements(ctx); err != nil {
			base.Fatalf("go: %v", err)
		}
	}

	return matches, loadedPackages
}

// matchLocalDirs类似于m.MatchDirs，但试图避免扫描标准库和活动模块之外的目录。
func matchLocalDirs(ctx context.Context, modRoots []string, m *search.Match, rs *Requirements) {
	if !m.IsLocal() {
		panic(fmt.Sprintf("internal error: resolveLocalDirs on non-local pattern %s", m.Pattern()))
	}

	if i := strings.Index(m.Pattern(), "..."); i >= 0 {
		// 模式是本地的，但它是一个通配符。其包将只解析为标准
		// 库、主模块或主
		// 模块的某些依赖项中的路径。在浏览文件系统之前，请先验证一下：在/var或/etc这样的目录中浏览文件系统可能非常昂贵！
		dir := filepath.Dir(filepath.Clean(m.Pattern()[:i+3]))
		absDir := dir
		if !filepath.IsAbs(dir) {
			absDir = filepath.Join(base.Cwd(), dir)
		}

		modRoot := findModuleRoot(absDir)
		found := false
		for _, mainModuleRoot := range modRoots {
			if mainModuleRoot == modRoot {
				found = true
				break
			}
		}
		if !found && search.InDir(absDir, cfg.GOROOTsrc) == "" && pathInModuleCache(ctx, absDir, rs) == "" {
			m.Dirs = []string{}
			scope := "main module or its selected dependencies"
			if inWorkspaceMode() {
				scope = "modules listed in go.work or their selected dependencies"
			}
			m.AddError(fmt.Errorf("directory prefix %s does not contain %s", base.ShortPath(absDir), scope))
			return
		}
	}

	m.MatchDirs(modRoots)
}

// resolveLocalPackage将文件系统路径解析为包路径。
func resolveLocalPackage(ctx context.Context, dir string, rs *Requirements) (string, error) {
	var absDir string
	if filepath.IsAbs(dir) {
		absDir = filepath.Clean(dir)
	} else {
		absDir = filepath.Join(base.Cwd(), dir)
	}

	bp, err := cfg.BuildContext.ImportDir(absDir, 0)
	if err != nil && (bp == nil || len(bp.IgnoredGoFiles) == 0) {
		// golang。org/issue/32917：只有当相对路径实际包含该包的代码
		// 时，我们才应该解析到该包路径的相对路径。
		// 
		// 如果指定的目录不存在或不包含Go文件，则
		// 包不存在。
		// 其他错误可能会影响包加载，但不会影响解析。
		if _, err := fsys.Stat(absDir); err != nil {
			if os.IsNotExist(err) {
				// 将特定于操作系统的错误规范化为errDirectoryNotFound，以便用户更容易搜索错误
				// 消息。
				return "", &fs.PathError{Op: "stat", Path: absDir, Err: errDirectoryNotFound}
			}
			return "", err
		}
		if _, noGo := err.(*build.NoGoError); noGo {
			// 一个不包含任何Go源文件的目录-甚至忽略了
			// 个不是Go包，我们无法将其解析为包
			// 路径，因为该路径可能由其他
			// 模块提供。
			// 
			// 任何其他错误表示包“存在”（至少在
			// 中，它不能存在于任何其他模块中），但存在其他一些
			// 问题（例如语法错误）。
			return "", err
		}
	}

	for _, mod := range MainModules.Versions() {
		modRoot := MainModules.ModRoot(mod)
		if modRoot != "" && absDir == modRoot {
			if absDir == cfg.GOROOTsrc {
				return "", errPkgIsGorootSrc
			}
			return MainModules.PathPrefix(mod), nil
		}
	}

	// 注意：这里对@here的检查只是为了避免误解
	// 模块缓存目录（以前是GOPATH/src/mod/foo@v1.5.2/酒吧）。
	// 严格来说，保留支票不是必要的，但有帮助。
	var pkgNotFoundErr error
	pkgNotFoundLongestPrefix := ""
	for _, mainModule := range MainModules.Versions() {
		modRoot := MainModules.ModRoot(mainModule)
		if modRoot != "" && strings.HasPrefix(absDir, modRoot+string(filepath.Separator)) && !strings.Contains(absDir[len(modRoot):], "@") {
			suffix := filepath.ToSlash(absDir[len(modRoot):])
			if strings.HasPrefix(suffix, "/vendor/") {
				if cfg.BuildMod != "vendor" {
					return "", fmt.Errorf("without -mod=vendor, directory %s has no package path", absDir)
				}

				readVendorList(mainModule)
				pkg := strings.TrimPrefix(suffix, "/vendor/")
				if _, ok := vendorPkgModule[pkg]; !ok {
					return "", fmt.Errorf("directory %s is not a package listed in vendor/modules.txt", absDir)
				}
				return pkg, nil
			}

			mainModulePrefix := MainModules.PathPrefix(mainModule)
			if mainModulePrefix == "" {
				pkg := strings.TrimPrefix(suffix, "/")
				if pkg == "builtin" {
					// /“builtin”是一个带有真实源文件的伪包。
					// 它不包含在“std”中，因此不应从“”解析
					// 在模块“std”内。
					return "", errPkgIsBuiltin
				}
				return pkg, nil
			}

			pkg := mainModulePrefix + suffix
			if _, ok, err := dirInModule(pkg, mainModulePrefix, modRoot, true); err != nil {
				return "", err
			} else if !ok {
				// 此主模块可能包含目录，但不包含。其他主
				// 模块可能包含目录，因此请等待我们完成循环
				// 以查看其他主模块是否包含目录。但如果没有，则返回一个错误。
				if len(mainModulePrefix) > len(pkgNotFoundLongestPrefix) {
					pkgNotFoundLongestPrefix = mainModulePrefix
					pkgNotFoundErr = &PackageNotInModuleError{MainModules: []module.Version{mainModule}, Pattern: pkg}
				}
				continue
			}
			return pkg, nil
		}
	}
	if pkgNotFoundErr != nil {
		return "", pkgNotFoundErr
	}

	if sub := search.InDir(absDir, cfg.GOROOTsrc); sub != "" && sub != "." && !strings.Contains(sub, "@") {
		pkg := filepath.ToSlash(sub)
		if pkg == "builtin" {
			return "", errPkgIsBuiltin
		}
		return pkg, nil
	}

	pkg := pathInModuleCache(ctx, absDir, rs)
	if pkg == "" {
		scope := "main module or its selected dependencies"
		if inWorkspaceMode() {
			scope = "modules listed in go.work or their selected dependencies"
		}
		return "", fmt.Errorf("directory %s outside %s", base.ShortPath(absDir), scope)
	}
	return pkg, nil
}

var (
	errDirectoryNotFound = errors.New("directory not found")
	errPkgIsGorootSrc    = errors.New("GOROOT/src is not an importable package")
	errPkgIsBuiltin      = errors.New(`"builtin" is a pseudo-package, not an importable package`)
)

// pathInModuleCache返回目录dir的导入路径，如果dir位于生成列表中某个模块的模块缓存副本中，则返回该目录的导入路径。
func pathInModuleCache(ctx context.Context, dir string, rs *Requirements) string {
	tryMod := func(m module.Version) (string, bool) {
		var root string
		var err error
		if repl := Replacement(m); repl.Path != "" && repl.Version == "" {
			root = repl.Path
			if !filepath.IsAbs(root) {
				root = filepath.Join(replaceRelativeTo(), root)
			}
		} else if repl.Path != "" {
			root, err = modfetch.DownloadDir(repl)
		} else {
			root, err = modfetch.DownloadDir(m)
		}
		if err != nil {
			return "", false
		}

		sub := search.InDir(dir, root)
		if sub == "" {
			return "", false
		}
		sub = filepath.ToSlash(sub)
		if strings.Contains(sub, "/vendor/") || strings.HasPrefix(sub, "vendor/") || strings.Contains(sub, "@") {
			return "", false
		}

		return path.Join(m.Path, filepath.ToSlash(sub)), true
	}

	if rs.pruning == pruned {
		for _, m := range rs.rootModules {
			if v, _ := rs.rootSelected(m.Path); v != m.Version {
				continue // m是根，但对于同一条路径，我们有一个更高的根。
			}
			if importPath, ok := tryMod(m); ok {
				// checkMultiplePaths确保一个模块最多可以用于一个
				// 需求，所以必须是这样。
				return importPath
			}
		}
	}

	// 没有包含dir的根，或者该图未运行（因此我们不希望
	// 区分根和传递依赖项）。无论哪种方式，
	// 检查完整的图表，看看目录是否是非根依赖项。
	// 
	// 如果根与完整的模块图不一致，则所选的
	// 根模块版本可能与我们前面检查的版本不同。
	// 也重新检查这些路径。

	mg, _ := rs.Graph(ctx)
	var importPath string
	for _, m := range mg.BuildList() {
		var found bool
		importPath, found = tryMod(m)
		if found {
			break
		}
	}
	return importPath
}

// ImportFromFiles根据需要将模块添加到构建列表中
// 以满足指定Go源文件中的导入。
// 
// 缺少依赖项中的错误被沉默。
// 
// TODO（bcmills）：沉默错误似乎是关闭的。仔细看看这一点，找出错误报告实际上应该是什么。
func ImportFromFiles(ctx context.Context, gofiles []string) {
	rs := LoadModFile(ctx)

	tags := imports.Tags()
	imports, testImports, err := imports.ScanFiles(gofiles, tags)
	if err != nil {
		base.Fatalf("go: %v", err)
	}

	loaded = loadFromRoots(ctx, loaderParams{
		PackageOpts: PackageOpts{
			Tags:                  tags,
			ResolveMissingImports: true,
			SilencePackageErrors:  true,
		},
		requirements: rs,
		listRoots: func(*Requirements) (roots []string) {
			roots = append(roots, imports...)
			roots = append(roots, testImports...)
			return roots
		},
	})
	requirements = loaded.requirements
}

// DirImportPath返回dir的有效导入路径，
// 如果它位于主模块内，则返回“”。
func (mms *MainModuleSet) DirImportPath(ctx context.Context, dir string) (path string, m module.Version) {
	if !HasModRoot() {
		return ".", module.Version{}
	}
	LoadModFile(ctx) // 设置targetPrefix。

	if !filepath.IsAbs(dir) {
		dir = filepath.Join(base.Cwd(), dir)
	} else {
		dir = filepath.Clean(dir)
	}

	var longestPrefix string
	var longestPrefixPath string
	var longestPrefixVersion module.Version
	for _, v := range mms.Versions() {
		modRoot := mms.ModRoot(v)
		if dir == modRoot {
			return mms.PathPrefix(v), v
		}
		if strings.HasPrefix(dir, modRoot+string(filepath.Separator)) {
			pathPrefix := MainModules.PathPrefix(v)
			if pathPrefix > longestPrefix {
				longestPrefix = pathPrefix
				longestPrefixVersion = v
				suffix := filepath.ToSlash(dir[len(modRoot):])
				if strings.HasPrefix(suffix, "/vendor/") {
					longestPrefixPath = strings.TrimPrefix(suffix, "/vendor/")
				}
				longestPrefixPath = mms.PathPrefix(v) + suffix
			}
		}
	}
	if len(longestPrefix) > 0 {
		return longestPrefixPath, longestPrefixVersion
	}

	return ".", module.Version{}
}

// ImportMap返回在源代码中找到的导入路径的实际包导入路径
// 。
// 如果给定的导入路径没有出现在源代码
// 对于已加载的包，ImportMap将返回空字符串。
func ImportMap(path string) string {
	pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
	if !ok {
		return ""
	}
	return pkg.path
}

// PackageDir返回包含导入路径命名的包的源代码
// 的目录。
func PackageDir(path string) string {
	pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
	if !ok {
		return ""
	}
	return pkg.dir
}

// PackageModule返回提供由导入路径命名的包的模块。
func PackageModule(path string) module.Version {
	pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
	if !ok {
		return module.Version{}
	}
	return pkg.mod
}

// Lookup返回源目录、导入路径以及
// 从parentDir中的包导入时路径处的包的任何加载错误。
// 查找要求此包中的一个加载函数已被调用。
func Lookup(parentPath string, parentIsStd bool, path string) (dir, realPath string, err error) {
	if path == "" {
		panic("Lookup called with empty package path")
	}

	if parentIsStd {
		path = loaded.stdVendor(parentPath, path)
	}
	pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
	if !ok {
		// 加载程序应该已经找到所有相关路径。
		// 不过也有一些例外情况：
		// -在不带-test的go list期间，处理p.TestImports和p.XTestImports的p.Resolve调用
		// 最终在这里规范化导入路径。
		// /-在任何加载过程中，像“不安全”这样的未加载包都会在这里结束。
		// /-在任何加载过程中，像“runtime/cgo”这样的内置依赖项都会在这里结束。
		// /-因为我们忽略了模块加载器中的appengine/*，所以任何实际appengine/*库的依赖项都会在这里结束。
		dir := findStandardImportPath(path)
		if dir != "" {
			return dir, path, nil
		}
		return "", "", errMissing
	}
	return pkg.dir, pkg.path, pkg.err
}

// 加载程序管理加载有关
// 特定构建所需软件包的信息的过程，
// 检查软件包在模块集中是否可用，
// 并在需要时更新模块集。
type loader struct {
	loaderParams

	// AllCloseSoverts表示“all”模式是否包括主模块外测试的依赖关系（如Go 1.11–1.15）。
	// （否则，与Go 1.16+一样，“all”模式仅包括由*主模块中的包和测试导入的包
	// /传递性地*）
	allClosesOverTests bool

	work *par.Queue

	// 每次迭代重置
	roots    []*loadPkg
	pkgCache *par.Cache // 包路径（字符串）→ *loadPkg 
	pkgs     []*loadPkg // 加载包和测试的传递闭包；在buildStacks中填充
}

// loaderParams配置加载程序实例加载的包和报告的属性
// by。
type loaderParams struct {
	PackageOpts
	requirements *Requirements

	allPatternIsRoot bool // “all”模式是附加根吗？

	listRoots func(rs *Requirements) []string
}

func (ld *loader) reset() {
	select {
	case <-ld.work.Idle():
	default:
		panic("loader.reset when not idle")
	}

	ld.roots = nil
	ld.pkgCache = new(par.Cache)
	ld.pkgs = nil
}

// errorf通过任一操作系统报告错误。标准或基础。错误，
// 根据ld。AllowErrors已设置。
func (ld *loader) errorf(format string, args ...any) {
	if ld.AllowErrors {
		fmt.Fprintf(os.Stderr, format, args...)
	} else {
		base.Errorf(format, args...)
	}
}

// loadPkg记录有关单个加载包的信息。
type loadPkg struct {
	// 在构建时填充：
	path   string // 导入路径
	testOf *loadPkg

	// 在构建时填充并由（*loader）更新。applyPkgFlags:
	flags atomicLoadPkgFlags

	// 由（*loader）填充。load:
	mod         module.Version // 模块提供包
	dir         string         // 包含源代码的目录
	err         error          // 错误加载包
	imports     []*loadPkg     // 由这一个导入的包
	testImports []string       // 仅测试导入，保存供pkg使用。测验
	inStd       bool
	altMods     []module.Version // 本可以包含包但没有包含的模块

	// 由（*loader）填充。pkgTest:
	testOnce sync.Once
	test     *loadPkg

	// 由（*loader）中的后处理填充。buildStacks:
	stack *loadPkg // package在这个pkg的最小导入堆栈中导入这个包
}

// loadPkgFlags是一组跟踪关于包的元数据的标志。
type loadPkgFlags int8

const (
	// pkgInAll表示包处于“all”包模式，
	// 无论我们是否正在加载“all”包模式。
	// 
	// 同时设置pkgInAll标志和pkgImportsLoaded标志时，设置最后一个标志的调用方
	// 必须将pkgInAll标志传播到标记包的所有导入项
	// 中。
	// 
	// 如果一个测试会将它导入的包升级为“all”（例如当测试本身在主
	// 模块中，或者ld.allclosesOverts为真时），那么该测试将被标记为pkgInAll。
	pkgInAll loadPkgFlags = 1 << iota

	// pkgIsRoot表示包与调用方请求的根包
	// 模式之一匹配。
	// 
	// 如果设置了LoadTests，那么当同时设置了pkgIsRoot和pkgImportsLoaded时，
	// 设置最后一个标志的调用方必须为
	// 包（在pkg.test字段中）填充一个测试。
	// 
	// 如果“all”模式包含为根，那么“all”
	// 中的非测试包也是根（并且必须标记为pkgiroot）。
	pkgIsRoot

	// pkgFromRoot表示包处于从根开始的导入的传递闭包中。（请注意，每个标记为pkgIsRoot 
	// 的包也被标记为pkgFromRoot。）
	pkgFromRoot

	// pkgImportsLoaded表示
	// loadPkg已填充。
	pkgImportsLoaded
)

// 报告cond中的所有标志是否都在f中设置。
func (f loadPkgFlags) has(cond loadPkgFlags) bool {
	return f&cond == cond
}

// 一个atomicLoadPkgFlags存储一个loadPkgFlags，其中可以以原子方式添加单个标志。
type atomicLoadPkgFlags struct {
	bits int32
}

// 更新在af中设置给定的标志（除了已设置的任何标志）。
// 
// update返回上一个标志状态，以便调用方可以确定新设置了哪些
// 标志。
func (af *atomicLoadPkgFlags) update(flags loadPkgFlags) (old loadPkgFlags) {
	for {
		old := atomic.LoadInt32(&af.bits)
		new := old | int32(flags)
		if new == old || atomic.CompareAndSwapInt32(&af.bits, old, new) {
			return loadPkgFlags(old)
		}
	}
}

// has报告cond中的所有标志是否在af中设置。
func (af *atomicLoadPkgFlags) has(cond loadPkgFlags) bool {
	return loadPkgFlags(atomic.LoadInt32(&af.bits))&cond == cond
}

// isTest报告pkg是否是另一个包的测试。
func (pkg *loadPkg) isTest() bool {
	return pkg.testOf != nil
}

// fromExternalModule报告pkg是否从主模块以外的模块加载。
func (pkg *loadPkg) fromExternalModule() bool {
	if pkg.mod.Path == "" {
		return false // 从标准库加载，而不是模块
	}
	return !MainModules.Contains(pkg.mod.Path)
}

var errMissing = errors.New("cannot find package")

// loadFromRoots尝试加载处理一组
// 根包及其依赖项所需的构建图。
// 
// 根包集由参数返回。listRoots函数和
// 根据需要通过跟踪导入（可能还有测试）
// 扩展到完整的包集。
func loadFromRoots(ctx context.Context, params loaderParams) *loader {
	ld := &loader{
		loaderParams: params,
		work:         par.NewQueue(runtime.GOMAXPROCS(0)),
	}

	if ld.GoVersion == "" {
		ld.GoVersion = MainModules.GoVersion()

		if ld.Tidy && semver.Compare("v"+ld.GoVersion, "v"+LatestGoVersion()) > 0 {
			ld.errorf("go: go.mod file indicates go %s, but maximum version supported by tidy is %s\n", ld.GoVersion, LatestGoVersion())
			base.ExitIfErrors()
		}
	}

	if ld.Tidy {
		if ld.TidyCompatibleVersion == "" {
			ld.TidyCompatibleVersion = priorGoVersion(ld.GoVersion)
		} else if semver.Compare("v"+ld.TidyCompatibleVersion, "v"+ld.GoVersion) > 0 {
			// Go工具链的每个版本都知道如何解释Go。mod和
			// 开始。所有以前版本生成的文件总和，因此兼容性
			// 版本高于go。mod版本没有添加任何内容。
			ld.TidyCompatibleVersion = ld.GoVersion
		}
	}

	if semver.Compare("v"+ld.GoVersion, narrowAllVersionV) < 0 && !ld.UseVendorAll {
		// 模块的go版本明显早于图形
		// 修剪的“all”更改，因此继续使用旧的解释。
		ld.allClosesOverTests = true
	}

	var err error
	desiredPruning := pruningForGoVersion(ld.GoVersion)
	if ld.requirements.pruning == workspace {
		desiredPruning = workspace
	}
	ld.requirements, err = convertPruning(ctx, ld.requirements, desiredPruning)
	if err != nil {
		ld.errorf("go: %v\n", err)
	}

	if ld.requirements.pruning == unpruned {
		// 如果模块图不支持修剪，我们假设需要完整的模块图来加载包依赖项。
		// 
		// 这可能不是严格必要的，但它符合历史上
		// go命令的行为并保持go。mod file在
		// 手工编辑错误的情况下更加一致——
		// 在不维护扩展go的模块中进行抽查的可能性较小。mod 
		// 图形修剪所需的要求。
		var err error
		ld.requirements, _, err = expandGraph(ctx, ld.requirements)
		if err != nil {
			ld.errorf("go: %v\n", err)
		}
	}

	for {
		ld.reset()

		// 加载根包及其导入。
		// 注意：返回的根在每次迭代时都可能发生变化，因为包模式的扩展取决于我们使用的构建列表。
		rootPkgs := ld.listRoots(ld.requirements)

		if ld.requirements.pruning == pruned && cfg.BuildMod == "mod" {
			// 在开始加载包的可传递导入之前，找到所有
			// 根包，并将其包含的模块升级为根模块
			// 依赖项。如果他们走了。mod文件是整洁的（常见情况），并且根包集没有改变，那么我们可以在第一次尝试时选择所有可传递导入的正确版本，并在一次迭代中完成加载。
			changedBuildList := ld.preloadRootModules(ctx, rootPkgs)
			if changedBuildList {
				// 构建列表已更改，因此根包集可能也已更改。重新开始，接受改变。（预加载根比加载完整导入图便宜得多，因此我们宁愿为额外的预加载迭代支付
				// 也不愿最终放弃加载完整迭代的结果。）
				continue
			}
		}

		inRoots := map[*loadPkg]bool{}
		for _, path := range rootPkgs {
			root := ld.pkg(ctx, path, pkgIsRoot)
			if !inRoots[root] {
				ld.roots = append(ld.roots, root)
				inRoots[root] = true
			}
		}

		// ld。pkg将导入的包添加到工作队列中，并调用applyPkgFlags、
		// 根据需要添加测试（和测试依赖项）。
		// 
		// 当队列中的所有工作完成后，我们将知道已加载依赖项的传递闭包。
		<-ld.work.Idle()

		ld.buildStacks()

		changed, err := ld.updateRequirements(ctx)
		if err != nil {
			ld.errorf("go: %v\n", err)
			break
		}
		if changed {
			// 在模块图稳定之前，不要解决缺少的导入。
			// 如果根仍在更改，它们可能会指定对缺少的包的
			// 要求，我们宁愿使用新根指定的
			// 版本，也不愿在
			// 不相关的版本上添加新的依赖项。
			continue
		}

		if !ld.ResolveMissingImports || (!HasModRoot() && !allowMissingModuleImports) {
			// 我们已经加载了尽可能多的内容，但没有解决缺少的导入问题。根是稳定的，我们已经解决了所有丢失的包。
			break
		}

		modAddedBy := ld.resolveMissingImports(ctx)
		if len(modAddedBy) == 0 {
			break
		}

		toAdd := make([]module.Version, 0, len(modAddedBy))
		for m, _ := range modAddedBy {
			toAdd = append(toAdd, m)
		}
		module.Sort(toAdd) // 要出错，请确定

		// 我们在解决缺少的导入之前运行了更新需求，但它没有进行任何更改，因此我们知道需求图已经与ld一致。我们不需要通过ld。包裹再次发送至updateRoots 
		// 。（这将浪费时间寻找我们已经应用
		// 的更改。）
		var noPkgs []*loadPkg
		// 我们还知道下一次我们将再次调用updateRequirements 
		// 迭代，所以我们不需要在这里进行更新。（那会浪费时间
		// 计算一个“直接”映射，我们以后无论如何都要重新计算它。）
		direct := ld.requirements.direct
		rs, err := updateRoots(ctx, direct, ld.requirements, noPkgs, toAdd, ld.AssumeRootsImported)
		if err != nil {
			// 如果在新添加的模块中发现错误，请报告包
			// 导入堆栈，而不是模块需求堆栈。软件包
			// 更具描述性。
			if err, ok := err.(*mvs.BuildListError); ok {
				if pkg := modAddedBy[err.Module()]; pkg != nil {
					ld.errorf("go: %s: %v\n", pkg.stackText(), err.Err)
					break
				}
			}
			ld.errorf("go: %v\n", err)
			break
		}
		if reflect.DeepEqual(rs.rootModules, ld.requirements.rootModules) {
			// 有些地方出了大问题。resolveMissingImports为我们提供了一个非空的
			// 模块集，以添加到图表中，但添加这些模块没有
			// 效果-要么它们已经在图表中，要么UpdateRoot没有按要求添加它们。gong zuo jian defg
			panic(fmt.Sprintf("internal error: adding %v to module graph had no effect on root requirements (%v)", toAdd, rs.rootModules))
		}
		ld.requirements = rs
	}

	// 在我们报告错误之前，请整理构建列表（如果适用）。
	// /（整理过程可能会从无关依赖项中删除错误。）
	if ld.Tidy {
		rs, err := tidyRoots(ctx, ld.requirements, ld.pkgs)
		if err != nil {
			ld.errorf("go: %v\n", err)
		}

		if ld.requirements.pruning == pruned {
			// 我们不断为ld添加整齐的根。加载期间的要求，因此在
			// 这一点上，整齐的根应该是
			// ld根的子集。要求，确保在
			// 图形修剪范围内没有新的依赖项。
			// 如果不是这样，则上面的加载循环中存在错误。
			for _, m := range rs.rootModules {
				if v, ok := ld.requirements.rootSelected(m.Path); !ok || v != m.Version {
					ld.errorf("go: internal error: a requirement on %v is needed but was not added during package loading\n", m)
					base.ExitIfErrors()
				}
			}
		}
		ld.requirements = rs
	}

	// 如果有错误，请报告。
	for _, pkg := range ld.pkgs {
		if pkg.err == nil {
			continue
		}

		// 将导入程序信息添加到校验和错误中。
		if sumErr := (*ImportMissingSumError)(nil); errors.As(pkg.err, &sumErr) {
			if importer := pkg.stack; importer != nil {
				sumErr.importer = importer.path
				sumErr.importerVersion = importer.mod.Version
				sumErr.importerIsTest = importer.testOf != nil
			}
		}

		if ld.SilencePackageErrors {
			continue
		}
		if stdErr := (*ImportMissingError)(nil); errors.As(pkg.err, &stdErr) &&
			stdErr.isStd && ld.SilenceMissingStdImports {
			continue
		}
		if ld.SilenceNoGoErrors && errors.Is(pkg.err, imports.ErrNoGo) {
			continue
		}

		ld.errorf("%s: %v\n", pkg.stackText(), pkg.err)
	}

	ld.checkMultiplePaths()
	return ld
}

// 更新要求确保。要求与
// 从ld获得的信息一致。pkgs。
// 
// 尤其是：
// 
// -提供从主模块直接导入的包的模块被标记为直接，并被提升为显式根。如果由于-mod=readonly或-mod=vendor而无法升级所需的根
// 程序包，则导入的
// 程序包将标记为错误。
// 
// -如果ld扫描的是独立于构建约束的“all”模式，则
// /保证已看到每个直接导入。
// 未提供任何直接导入包的模块依赖项将标记为间接。
// 
// -根依赖项将更新为其选定的版本。
// 
// “更改”返回值报告更新是否更改了所选
// 任何模块的版本，该模块提供了已加载的包，或者现在可能提供了以前未解析的包。
func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err error) {
	rs := ld.requirements

	// direct包含据信可直接提供包的模块集
	// 由主模块导入。
	var direct map[string]bool

	// 如果我们没有扫描主模块的所有导入，或者没有使用
	// 导入。AnyTags，那么我们不必加载
	// 提供“直接”导入的每个包——因此我们无法在ld中安全地标记现有的直接
	// 依赖项。要求仅作为间接要求。直接传播它们。
	loadedDirect := ld.allPatternIsRoot && reflect.DeepEqual(ld.Tags, imports.AnyTags())
	if loadedDirect {
		direct = make(map[string]bool)
	} else {
		// TODO（bcmills）：当
		// 它实际上很少与rs.direct发生变化时，在这里分配和复制一张地图似乎是一种耻辱。衡量这一成本，
		// 或许可以避免复制。
		direct = make(map[string]bool, len(rs.direct))
		for mPath := range rs.direct {
			direct[mPath] = true
		}
	}

	for _, pkg := range ld.pkgs {
		if pkg.mod.Version != "" || !MainModules.Contains(pkg.mod.Path) {
			continue
		}
		for _, dep := range pkg.imports {
			if !dep.fromExternalModule() {
				continue
			}

			if inWorkspaceMode() {
				// 在工作区模式/工作区修剪模式下，根是主模块
				// 而不是主模块的直接依赖项。以下选中的
				// 根不适用。
				if mg, err := rs.Graph(ctx); err != nil {
					return false, err
				} else if _, ok := mg.RequiredBy(dep.mod); !ok {
					// dep.mod不是一个显式依赖项，但必须是。
					// 请参阅下面返回的错误评论。
					pkg.err = &DirectImportFromImplicitDependencyError{
						ImporterPath: pkg.path,
						ImportedPath: dep.path,
						Module:       dep.mod,
					}
				}
				continue
			}

			if pkg.err == nil && cfg.BuildMod != "mod" {
				if v, ok := rs.rootSelected(dep.mod.Path); !ok || v != dep.mod.Version {
					// dep.mod不是一个显式依赖项，但必须是。
					// 因为我们没有处于“mod”模式，我们将无法更新它。
					// 相反，请在导入包上标记一个错误。
					// 
					// TODO（#41688）：生成的错误消息未能包含导入语句的文件
					// （因为模块加载程序没有跟踪该信息）。想一想如何将进口
					// 定位通过。
					pkg.err = &DirectImportFromImplicitDependencyError{
						ImporterPath: pkg.path,
						ImportedPath: dep.path,
						Module:       dep.mod,
					}
					// cfg。BuildMod不允许我们将dep.mod更改为直接
					// 依赖项，因此不要将其标记为直接
					// 依赖项。
					continue
				}
			}

			// dep是由主
			// 模块中的一个包或测试直接导入，并从其他模块（非标准库）加载的包。
			// 将其模块标记为直接依赖项。
			direct[dep.mod.Path] = true
		}
	}

	var addRoots []module.Version
	if ld.Tidy {
		// 当我们用修剪过的依赖关系图整理模块时，我们可能需要
		// 添加根以保留间接的、仅测试的依赖关系的版本
		// 升级到更高级别或在运行中丢失。
		// 直接依赖的mod文件。（例如，直接依赖项可能是一个非常稳定的代码基，早于模块，因此没有go.mod文件，或者直接依赖项的作者可能忘记了将更改提交到go.mod文件，或者可能进行了错误的手动编辑，导致了不整洁。）
		// 
		// 将间接依赖项提升到根目录会将其下一层
		// 依赖项添加到模块图中，这可能会增加我们已经从中加载包的其他模块的所选
		// 版本。
		// 因此，在我们将一个间接依赖项提升到根之后，我们需要重新加载
		// 包，这意味着加载的另一个迭代。
		// 
		// 作为一个额外的问题，由于升级根目录而进行的升级可能会导致
		// 以前解决的包无法解决。例如，提供不稳定包的
		// 模块可能升级到不再包含该包的版本
		// 中。如果我们随后解决缺少的
		// 包，我们可能会添加另一个根，以升级其他
		// 依赖项。（mod_tidy_convergence*.txt中的测试说明了一些
		// 尤其令人担忧的情况。）
		// 
		// 为了确保升级、添加和升级根的过程最终终止，在迭代过程中，我们只向
		// 根集添加模块-我们只在
		// 迭代的最后删除不相关的根，在我们已经在（最终）整洁的根集中添加了我们计划需要的每个根之后。
		// 
		// 由于我们在迭代期间不删除任何根，即使它们不再提供任何导入的包，
		// 根的选定版本只能增加，根集只能扩展。现存根路径的集合
		// 是有限的，每个路径的版本集合是
		// 有限的，因此迭代*必须*达到一个稳定的不动点。我们甚至不知道根需求应该是什么，所以我们无法继续加载。将错误返回给调用者
		tidy, err := tidyRoots(ctx, rs, ld.pkgs)
		if err != nil {
			return false, err
		}
		addRoots = tidy.rootModules
	}

	rs, err = updateRoots(ctx, direct, rs, ld.pkgs, addRoots, ld.AssumeRootsImported)
	if err != nil {
		return false, err
	}

	if rs != ld.requirements && !reflect.DeepEqual(rs.rootModules, ld.requirements.rootModules) {
		// 模块图的根以某种方式发生了更改（不仅仅是
		// “direct”标记）。检查更改是否影响了任何加载的
		// 包。
		mg, err := rs.Graph(ctx)
		if err != nil {
			return false, err
		}
		for _, pkg := range ld.pkgs {
			if pkg.fromExternalModule() && mg.Selected(pkg.mod.Path) != pkg.mod.Version {
				changed = true
				break
			}
			if pkg.err != nil {
				// 将模块升级到根目录可能会解决以前缺少的导入（通过引入
				// 提供的以前修剪的依赖项）或不明确的导入（通过将一个
				// 替代项升级到根目录并忽略第二级替代项）或
				// 否则出错（从一个不能被
				// 获取的版本升级到一个可以被获取的版本）。
				// 
				// 我们将不枚举所有可能的错误，而是检查
				// importFromModules是否为包返回nil。
				// 误报是可以的：如果我们这里有误报，我们这次将执行
				// 额外的包加载迭代，但当根集停止更改时，我们仍将
				// 收敛。
				// 
				// 在某种意义上，我们可以将其视为“升级了提供
				// pkg的模块”。从“无”到高于“无”的版本的路径。
				if _, _, _, err = importFromModules(ctx, pkg.path, rs, nil); err == nil {
					changed = true
					break
				}
			}
		}
	}

	ld.requirements = rs
	return changed, nil
}

// resolveMissingImports返回一组模块，这些模块可以作为
// 依赖项添加，以解析PKG中缺少的包。
// 
// 新解析的包将添加到addedModuleFor map，
// resolveMissingImports将从每个新模块版本返回到
// 模块将解析的第一个缺失包的映射。
func (ld *loader) resolveMissingImports(ctx context.Context) (modAddedBy map[module.Version]*loadPkg) {
	type pkgMod struct {
		pkg *loadPkg
		mod *module.Version
	}
	var pkgMods []pkgMod
	for _, pkg := range ld.pkgs {
		if pkg.err == nil {
			continue
		}
		if pkg.isTest() {
			// 如果我们缺少一个测试，我们也缺少它的非测试版本，
			// 我们应该只添加一次缺少的导入。
			continue
		}
		if !errors.As(pkg.err, new(*ImportMissingError)) {
			// 将其他错误留给导入或加载。要报告的文件包。
			continue
		}

		pkg := pkg
		var mod module.Version
		ld.work.Add(func() {
			var err error
			mod, err = queryImport(ctx, pkg.path, ld.requirements)
			if err != nil {
				var ime *ImportMissingError
				if errors.As(err, &ime) {
					for curstack := pkg.stack; curstack != nil; curstack = curstack.stack {
						if MainModules.Contains(curstack.mod.Path) {
							ime.ImportingMainModule = curstack.mod
							break
						}
					}
				}
				// 包装。err已经非nil，因此我们可以合理地将pkg的错误
				// 归因于原始错误或
				// queryImport返回的错误。现有错误只表明我们找不到
				// 这个包，而查询错误也解释了为什么我们没有修复
				// 这个问题，所以我们更喜欢后者。
				pkg.err = err
			}

			// err为零，但我们有意离开pkg。err non nil和pkg。mod 
			// unset：我们仍然没有满足
			// 成功加载包的其他不变量，例如扫描和加载该包的导入
			// 的不变量。如果我们成功解析了新的依赖关系图，
			// 调用者可以重新加载pkg并在此时更新错误。
			// 
			// 即使如此，也可能无法从我们在此处识别的版本加载包。模块可能会被其他依赖项升级，
			// 或者被mod本身的可传递依赖项升级，或者——不太可能——
			// 包可能会被AllowPackage钩子拒绝，或者被其他一些新添加或新升级的依赖项使
			// 变得模糊。
		})

		pkgMods = append(pkgMods, pkgMod{pkg: pkg, mod: &mod})
	}
	<-ld.work.Idle()

	modAddedBy = map[module.Version]*loadPkg{}
	for _, pm := range pkgMods {
		pkg, mod := pm.pkg, *pm.mod
		if mod.Path == "" {
			continue
		}

		fmt.Fprintf(os.Stderr, "go: found %s in %s %s\n", pkg.path, mod.Path, mod.Version)
		if modAddedBy[mod] == nil {
			modAddedBy[mod] = pkg
		}
	}

	return modAddedBy
}

// pkg为路径定位*loadPkg，如果需要，创建并排队加载它，并更新其状态以反映给定的标志。
// 
// 返回的*loadPkg导入将异步加载到
// ld中。
// 导入解决后，也将填充工作队列及其测试（如果请求）。当我。工作闲置，
// 的所有可传递导入将已加载请求的包（及其测试，如果请求）。
func (ld *loader) pkg(ctx context.Context, path string, flags loadPkgFlags) *loadPkg {
	if flags.has(pkgImportsLoaded) {
		panic("internal error: (*loader).pkg called with pkgImportsLoaded flag set")
	}

	pkg := ld.pkgCache.Do(path, func() any {
		pkg := &loadPkg{
			path: path,
		}
		ld.applyPkgFlags(ctx, pkg, flags)

		ld.work.Add(func() { ld.load(ctx, pkg) })
		return pkg
	}).(*loadPkg)

	ld.applyPkgFlags(ctx, pkg, flags)
	return pkg
}

// applyPkgFlags更新pkg。设置给定标志并传播这些标志的
// （传递性）效果的标志，可能会因此加载或排队进一步的
// 包。
func (ld *loader) applyPkgFlags(ctx context.Context, pkg *loadPkg, flags loadPkgFlags) {
	if flags == 0 {
		return
	}

	if flags.has(pkgInAll) && ld.allPatternIsRoot && !pkg.isTest() {
		// 由于处于“all”状态，该包与根模式匹配。
		flags |= pkgIsRoot
	}
	if flags.has(pkgIsRoot) {
		flags |= pkgFromRoot
	}

	old := pkg.flags.update(flags)
	new := old | flags
	if new == old || !new.has(pkgImportsLoaded) {
		// 我们要么没有更改pkg的状态，要么我们对
		// 的依赖项一无所知。不管怎样，我们都无法有效地加载它的测试或更新它的依赖项。
		return
	}

	if !pkg.isTest() {
		// 检查我们是否应该为pkg添加测试（或更新标记）。
		// ld。pkgTest是幂等的，额外的调用也很便宜，
		// 因此，如果我们对它的调用超出了严格的必要性，那也没关系。
		wantTest := false
		switch {
		case ld.allPatternIsRoot && MainModules.Contains(pkg.mod.Path):
			// 我们正在加载“all”模式，其中包括
			// 在主模块中测试导入的包。这个包在主模块中，所以我们
			// 需要识别其测试的导入，即使没有设置LoadTests。
			// 
			// （我们将在computePatternAll中明确过滤掉额外的测试。）
			wantTest = true

		case ld.allPatternIsRoot && ld.allClosesOverTests && new.has(pkgInAll):
			// 这个“all”模式的变体包括导入每个
			// 包的测试，该包本身在“all”中，而pkg在“all”中，所以它的测试也是
			// 也在“all”中（如上所述）。
			wantTest = true

		case ld.LoadTests && new.has(pkgIsRoot):
			// LoadTest明确请求对“根包”进行测试。
			wantTest = true
		}

		if wantTest {
			var testFlags loadPkgFlags
			if MainModules.Contains(pkg.mod.Path) || (ld.allClosesOverTests && new.has(pkgInAll)) {
				// 主模块中的包测试为“全部”，即
				// 它们导致导入的包也为“全部”。如果“all”关闭了测试依赖项，那么“all”中包的测试
				// 也是如此。
				testFlags |= pkgInAll
			}
			ld.pkgTest(ctx, pkg, testFlags)
		}
	}

	if new.has(pkgInAll) && !old.has(pkgInAll|pkgImportsLoaded) {
		// 我们刚刚用pkgInAll标记了pkg，或者我们刚刚加载了其
		// 导入，或者两者兼而有之。现在是将pkgInAll推广到进口产品的时候了。
		for _, dep := range pkg.imports {
			ld.applyPkgFlags(ctx, dep, pkgInAll)
		}
	}

	if new.has(pkgFromRoot) && !old.has(pkgFromRoot|pkgImportsLoaded) {
		for _, dep := range pkg.imports {
			ld.applyPkgFlags(ctx, dep, pkgFromRoot)
		}
	}
}

// preload rootmodules加载识别
// 在rootPkgs中提供包的每个模块的选定版本所需的模块要求，
// 如果需要，在模块图中添加新的根模块。
func (ld *loader) preloadRootModules(ctx context.Context, rootPkgs []string) (changedBuildList bool) {
	needc := make(chan map[module.Version]bool, 1)
	needc <- map[module.Version]bool{}
	for _, path := range rootPkgs {
		path := path
		ld.work.Add(func() {
			// 首先，尝试仅使用根来识别包含包的模块。
			// 
			// 如果主模块是整洁的，并且包是“全部”的——或者如果我们是
			// 幸运的话——我们可以识别其所有导入，而无需实际加载
			// 完整的模块图。
			m, _, _, err := importFromModules(ctx, path, ld.requirements, nil)
			if err != nil {
				var missing *ImportMissingError
				if errors.As(err, &missing) && ld.ResolveMissingImports {
					// 此软件包不是由任何选定的模块提供的。
					// 如果我们能找到它，它将是一个新的根依赖项。
					m, err = queryImport(ctx, path, ld.requirements)
				}
				if err != nil {
					// 我们无法识别包含此包的根模块。
					// 悬而未决；我们将在装货时报告。
					return
				}
			}
			if m.Path == "" {
				// 包装为std或cmd格式。我们不需要改变根集。
				return
			}

			v, ok := ld.requirements.rootSelected(m.Path)
			if !ok || v != m.Version {
				// 我们在m中找到了请求的包，但m不是根，因此
				// loadModGraph将不会加载其需求。我们需要将
				// 模块升级为root，以确保此包
				// 导入的任何其他包都是从正确的依赖版本解析的。
				// 
				// （这是
				// https:
				need := <-needc
				need[m] = true
				needc <- need
			}
		})
	}
	<-ld.work.Idle()

	need := <-needc
	if len(need) == 0 {
		return false // 无需添加根。
	}

	toAdd := make([]module.Version, 0, len(need))
	for m := range need {
		toAdd = append(toAdd, m)
	}
	module.Sort(toAdd)

	rs, err := updateRoots(ctx, ld.requirements.direct, ld.requirements, nil, toAdd, ld.AssumeRootsImported)
	if err != nil {
		// 我们缺少一些根依赖项，由于某些原因，我们无法加载足够的模块依赖关系图来添加缺少的根。Package 
		// 加载注定会失败，所以很快就会失败。
		ld.errorf("go: %v\n", err)
		base.ExitIfErrors()
		return false
	}
	if reflect.DeepEqual(rs.rootModules, ld.requirements.rootModules) {
		// 有些地方出了大问题。resolveMissingImports为我们提供了一个非空的
		// 模块集以添加到图形中，但添加这些模块没有
		// 效果-要么它们已经在图形中，要么UpdateRoot没有按要求添加它们。
		panic(fmt.Sprintf("internal error: adding %v to module graph had no effect on root requirements (%v)", toAdd, rs.rootModules))
	}

	ld.requirements = rs
	return true
}

// 加载单个包。
func (ld *loader) load(ctx context.Context, pkg *loadPkg) {
	var mg *ModuleGraph
	if ld.requirements.pruning == unpruned {
		var err error
		mg, err = ld.requirements.Graph(ctx)
		if err != nil {
			// 我们已经在loadFromRoots和/或
			// updateRequirements中检查了图形中的错误，所以我们故意忽略了错误，我们应该继续努力克服它。
			// 
			// 但是，由于mg可能不完整（因此可能会选择不准确的
			// 版本），我们不应该使用它来加载包。相反，我们传递一个nil 
			// /*ModuleGraph，这将导致mg首先尝试仅从
			// /主模块和根依赖项加载。
			mg = nil
		}
	}

	pkg.mod, pkg.dir, pkg.altMods, pkg.err = importFromModules(ctx, pkg.path, ld.requirements, mg)
	if pkg.dir == "" {
		return
	}
	if MainModules.Contains(pkg.mod.Path) {
		// 继续，将pkg标记为“全部”。这提供了一个不变量，即一个
		// 程序包在“all”中由其他程序包*仅*导入时，在加载其导入之前，始终将其标记为
		// 这样。
		// 
		// 目前我们实际上并不依赖于该不变量，但它可能会在一定程度上提高效率，并使行为更容易解释
		// （通过减少依赖项的标记位的波动），成本
		// 基本上什么都没有（与扫描源代码以获取导入相比，这些原子标志操作基本上是免费的）。
		ld.applyPkgFlags(ctx, pkg, pkgInAll)
	}
	if ld.AllowPackage != nil {
		if err := ld.AllowPackage(ctx, pkg.path, pkg.mod); err != nil {
			pkg.err = err
		}
	}

	pkg.inStd = (search.IsStandardImportPath(pkg.path) && search.InDir(pkg.dir, cfg.GOROOTsrc) != "")

	var imports, testImports []string

	if cfg.BuildContext.Compiler == "gccgo" && pkg.inStd {
		// 我们无法扫描gccgo的标准包。
	} else {
		var err error
		imports, testImports, err = scanDir(pkg.dir, ld.Tags)
		if err != nil {
			pkg.err = err
			return
		}
	}

	pkg.imports = make([]*loadPkg, 0, len(imports))
	var importFlags loadPkgFlags
	if pkg.flags.has(pkgInAll) {
		importFlags = pkgInAll
	}
	for _, path := range imports {
		if pkg.inStd {
			// 从“std”和“cmd”中的包导入时，应使用
			// GOROOT/src/vendor解决，即使“std”不是主模块。
			path = ld.stdVendor(pkg.path, path)
		}
		pkg.imports = append(pkg.imports, ld.pkg(ctx, path, importFlags))
	}
	pkg.testImports = testImports

	ld.applyPkgFlags(ctx, pkg, pkgImportsLoaded)
}

// pkgTest定位pkg的测试，根据需要创建它，并更新其状态
// 以反映给定的标志。
// 
// pkgTest要求已加载导入的pkg（标记为
// pkgImportsLoaded）。
func (ld *loader) pkgTest(ctx context.Context, pkg *loadPkg, testFlags loadPkgFlags) *loadPkg {
	if pkg.isTest() {
		panic("pkgTest called on a test package")
	}

	createdTest := false
	pkg.testOnce.Do(func() {
		pkg.test = &loadPkg{
			path:   pkg.path,
			testOf: pkg,
			mod:    pkg.mod,
			dir:    pkg.dir,
			err:    pkg.err,
			inStd:  pkg.inStd,
		}
		ld.applyPkgFlags(ctx, pkg.test, testFlags)
		createdTest = true
	})

	test := pkg.test
	if createdTest {
		test.imports = make([]*loadPkg, 0, len(pkg.testImports))
		var importFlags loadPkgFlags
		if test.flags.has(pkgInAll) {
			importFlags = pkgInAll
		}
		for _, path := range pkg.testImports {
			if pkg.inStd {
				path = ld.stdVendor(test.path, path)
			}
			test.imports = append(test.imports, ld.pkg(ctx, path, importFlags))
		}
		pkg.testImports = nil
		ld.applyPkgFlags(ctx, test, pkgImportsLoaded)
	} else {
		ld.applyPkgFlags(ctx, test, testFlags)
	}

	return test
}

// stdVendor从parentPath的标准库包导入时，返回具有给定
// 路径的包的规范导入路径。
func (ld *loader) stdVendor(parentPath, path string) string {
	if search.IsStandardImportPath(path) {
		return path
	}

	if str.HasPathPrefix(parentPath, "cmd") {
		if !ld.VendorModulesInGOROOTSrc || !MainModules.Contains("cmd") {
			vendorPath := pathpkg.Join("cmd", "vendor", path)

			if _, err := os.Stat(filepath.Join(cfg.GOROOTsrc, filepath.FromSlash(vendorPath))); err == nil {
				return vendorPath
			}
		}
	} else if !ld.VendorModulesInGOROOTSrc || !MainModules.Contains("std") || str.HasPathPrefix(parentPath, "vendor") {
		// 如果我们在'std'模块之外，请将从'std'
		// 内的导入解析为供应商目录。
		// 
		// 对以前缀“vendor/”开头的进口商也要这样做，即使我们
		// 位于“std”模块的*内部：从GOROOT/src/vendor全局解析
		// 的“vendor/”包（并作为“go list std”的一部分列出）
		// 与真正的模块依赖项不同，无法从实际模块导入
		// 内部包。
		// 
		// （请注意，尽管'vendor/'程序包与'std'*程序包*
		// 模式匹配，但它们不是std*模块*的一部分，在std内工作时不影响
		// /'go mod tidy'和类似的模块命令。）
		vendorPath := pathpkg.Join("vendor", path)
		if _, err := os.Stat(filepath.Join(cfg.GOROOTsrc, filepath.FromSlash(vendorPath))); err == nil {
			return vendorPath
		}
	}

	// 不出售：从模块解析。
	return path
}

// computePatternAll返回与模式“all”匹配的包列表，
// 从主模块中包的导入路径列表开始。
func (ld *loader) computePatternAll() (all []string) {
	for _, pkg := range ld.pkgs {
		if pkg.flags.has(pkgInAll) && !pkg.isTest() {
			all = append(all, pkg.path)
		}
	}
	sort.Strings(all)
	return all
}

// checkMultiplePaths验证给定模块路径是否用作其自身
// 或用作另一个模块的替换，但不能同时用作两者。
// 
// （见https:
func (ld *loader) checkMultiplePaths() {
	mods := ld.requirements.rootModules
	if cached := ld.requirements.graph.Load(); cached != nil {
		if mg := cached.(cachedGraph).mg; mg != nil {
			mods = mg.BuildList()
		}
	}

	firstPath := map[module.Version]string{}
	for _, mod := range mods {
		src := resolveReplacement(mod)
		if prev, ok := firstPath[src]; !ok {
			firstPath[src] = mod.Path
		} else if prev != mod.Path {
			ld.errorf("go: %s@%s used for two different module paths (%s and %s)\n", src.Path, src.Version, prev, mod.Path)
		}
	}
}

// 如果任何包将从
// rs下的不同模块加载，则checkTidyCompatibility将发出错误。要求。
func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements) {
	suggestUpgrade := false
	suggestEFlag := false
	suggestFixes := func() {
		if ld.AllowErrors {
			// 用户明确忽略了这些错误，所以不要用
			// 其他选项来打扰他们。
			return
		}

		// 我们直接打印到操作系统。因为这些信息是关于
		// 如何修复错误的建议，而不是错误本身。
		// /（实际错误应该已经记录。）

		fmt.Fprintln(os.Stderr)

		goFlag := ""
		if ld.GoVersion != MainModules.GoVersion() {
			goFlag = " -go=" + ld.GoVersion
		}

		compatFlag := ""
		if ld.TidyCompatibleVersion != priorGoVersion(ld.GoVersion) {
			compatFlag = " -compat=" + ld.TidyCompatibleVersion
		}
		if suggestUpgrade {
			eDesc := ""
			eFlag := ""
			if suggestEFlag {
				eDesc = ", leaving some packages unresolved"
				eFlag = " -e"
			}
			fmt.Fprintf(os.Stderr, "To upgrade to the versions selected by go %s%s:\n\tgo mod tidy%s -go=%s && go mod tidy%s -go=%s%s\n", ld.TidyCompatibleVersion, eDesc, eFlag, ld.TidyCompatibleVersion, eFlag, ld.GoVersion, compatFlag)
		} else if suggestEFlag {
			// 如果缺少一些软件包，但没有升级任何软件包，那么我们
			// 不应该明确建议升级到Go 1.16版本——即
			// 实际上不会为Go 1.16用户修复任何问题，并且*会*破坏
			// Go 1.17用户用的东西。
			fmt.Fprintf(os.Stderr, "To proceed despite packages unresolved in go %s:\n\tgo mod tidy -e%s%s\n", ld.TidyCompatibleVersion, goFlag, compatFlag)
		}

		fmt.Fprintf(os.Stderr, "If reproducibility with go %s is not needed:\n\tgo mod tidy%s -compat=%s\n", ld.TidyCompatibleVersion, goFlag, ld.GoVersion)

		// TODO（#46141）：填充链接的wiki页面。
		fmt.Fprintf(os.Stderr, "For other options, see:\n\thttps:// golang。org/doc/modules/pruning\n“）
	}

	mg, err := rs.Graph(ctx)
	if err != nil {
		ld.errorf("go: error loading go %s module graph: %v\n", ld.TidyCompatibleVersion, err)
		suggestFixes()
		return
	}

	// 并行重新解析包。
	// 
	// 我们重新解析每个包，而不仅仅是检查版本，以确保
	// 我们已经获取了模块源代码（重要的是，
	// 该源代码的校验和）对于确保
	// 进口明确无误所需的所有模块。这也产生了更清晰的诊断，因为我们可以说
	// 如果包裹变得模棱两可或完全消失，那么它到底发生了什么。
	// 
	// 我们并行重新解析包，因为这个过程涉及磁盘
	// I/O以检查包源，由于检查
	// 模棱两可的导入过程可能需要我们下载其他
	// 模块，否则将在Go 1.17中删除——我们不想在等待新的下载时阻止其他
	// 包的进展。
	type mismatch struct {
		mod module.Version
		err error
	}
	mismatchMu := make(chan map[*loadPkg]mismatch, 1)
	mismatchMu <- map[*loadPkg]mismatch{}
	for _, pkg := range ld.pkgs {
		if pkg.mod.Path == "" && pkg.err == nil {
			// 此软件包来自标准库（标准库与
			// 模块图相同）。
			continue
		}

		pkg := pkg
		ld.work.Add(func() {
			mod, _, _, err := importFromModules(ctx, pkg.path, rs, mg)
			if mod != pkg.mod {
				mismatches := <-mismatchMu
				mismatches[pkg] = mismatch{mod: mod, err: err}
				mismatchMu <- mismatches
			}
		})
	}
	<-ld.work.Idle()

	mismatches := <-mismatchMu
	if len(mismatches) == 0 {
		// 由于我们是作为“go mod tidy”的一部分运行的，模块
		// 图的根应该只包含与
		// 包图中的某个包相关的模块。我们检查了包图中的每个包，
		// 没有发现任何不匹配，因此这一定意味着
		// 模块图的所有根都是一致的。
		// 
		// 如果我们错了，Go 1.16 in-mod=readonly模式将与
		// “需要更新Go.mod”一起出错，这将非常混乱。因此，我们将再次检查上述推理是否成立——如果它不成立，我们将发出一个内部错误，希望用户将其报告为错误。
		for _, m := range ld.requirements.rootModules {
			if v := mg.Selected(m.Path); v != m.Version {
				fmt.Fprintln(os.Stderr)
				base.Fatalf("go: internal error: failed to diagnose selected-version mismatch for module %s: go %s selects %s, but go %s selects %s\n\tPlease report this at https:// golang。组织/问题。“，m.Path，ld.GoVersion，m.Version，ld.TidyCompatibleVersion，v）
			}
		}
		return
	}

	// 迭代包（而不是不匹配映射）以发出
	// 确定性顺序中的错误。
	for _, pkg := range ld.pkgs {
		mismatch, ok := mismatches[pkg]
		if !ok {
			continue
		}

		if pkg.isTest() {
			// 我们已经（或将）报告包本身的错误，
			// 所以不要报告重复的错误（以及更多的vebose）测试错误。
			if _, ok := mismatches[pkg.testOf]; !ok {
				base.Fatalf("go: internal error: mismatch recorded for test %s, but not its non-test package", pkg.path)
			}
			continue
		}

		switch {
		case mismatch.err != nil:
			// pkg已成功解决，但是使用rs.
			// 
			// 中的要求可能会出现错误，因为导入是由一个根提供的（因此，
			// 在具有修剪的模块图的主模块中是明确的），而
			// 也有一个或多个可传递的依赖项（在
			// 未运行的图中是模糊的）。
			// 
			// 也可能发生这种情况，因为一些可传递的依赖项将之前提供包的
			// 模块升级到一个
			// 不再提供的版本，或者升级到一个模块源代码（但
			// 不是孤立的go.mod文件）存在校验和错误的版本。
			if missing := (*ImportMissingError)(nil); errors.As(mismatch.err, &missing) {
				selected := module.Version{
					Path:    pkg.mod.Path,
					Version: mg.Selected(pkg.mod.Path),
				}
				ld.errorf("%s loaded from %v,\n\tbut go %s would fail to locate it in %s\n", pkg.stackText(), pkg.mod, ld.TidyCompatibleVersion, selected)
			} else {
				if ambiguous := (*AmbiguousImportError)(nil); errors.As(mismatch.err, &ambiguous) {
					// TODO:需要这张支票吗？
				}
				ld.errorf("%s loaded from %v,\n\tbut go %s would fail to locate it:\n\t%v\n", pkg.stackText(), pkg.mod, ld.TidyCompatibleVersion, mismatch.err)
			}

			suggestEFlag = true

			// 即使我们继续使用“-e”标志，旧版本也会在只读模式下出错，如果它认为正确的话。mod file包含
			// 任何不在其所选版本的*显式*依赖项，
			// 即使该依赖项与正在加载的任何包无关。
			// 
			// 我们在这里检查这种情况。如果所有根都是一致的
			// 那么“-e”标志就足够了，否则我们需要建议升级。
			if !suggestUpgrade {
				for _, m := range ld.requirements.rootModules {
					if v := mg.Selected(m.Path); v != m.Version {
						suggestUpgrade = true
						break
					}
				}
			}

		case pkg.err != nil:
			// pkg在使用修剪过的模块图时出错（可能使用-e标志抑制了
			// ），但使用未运行的图时错误消失了。
			// 
			// 如果在修剪后的图
			// （因为每个候选模块的“最新”版本要么不可用，要么不包含包），那么这是可能的，但是在
			// 未运行的图中得到解决，这是因为一个比最新版本更新的依赖项通常被
			// 删除。
			// 
			// 如果在修剪后的图中提供
			// 包的模块的源代码存在校验和错误，但未运行的
			// 图将该模块升级为具有正确校验和的版本，则也可能发生这种情况。
			// 
			// pkg。err应该已经记录在其他地方了——以及
			// 堆栈跟踪——所以这里只记录导入路径和非错误信息。
			suggestUpgrade = true
			ld.errorf("%s failed to load from any module,\n\tbut go %s would load it from %v\n", pkg.path, ld.TidyCompatibleVersion, mismatch.mod)

		case pkg.mod != mismatch.mod:
			// 两个Go版本都成功加载了该包，但每个版本中都有不同的模块。这可能会导致微妙的（甚至可能是
			// 不被注意！）使用不同
			// 工具链的构建之间的行为差异。
			suggestUpgrade = true
			ld.errorf("%s loaded from %v,\n\tbut go %s would select %v\n", pkg.stackText(), pkg.mod, ld.TidyCompatibleVersion, mismatch.mod.Version)

		default:
			base.Fatalf("go: internal error: mismatch recorded for package %s, but no differences found", pkg.path)
		}
	}

	suggestFixes()
	base.ExitIfErrors()
}

// 斯堪的纳维亚就像进口商品。ScanDir从列表中删除了已知的魔法导入，
// 这样我们就不会去寻找那些不存在的包了。
// 
// 标准的魔法输入是“C”，代表cgo。
// 
// 唯一已知的魔法进口是appengine和appengine/*。
// 它们太旧了，早于“go-get”，并且没有使用类似URL的路径。
// 现在大多数代码都使用谷歌。戈朗。org/appengine取而代之的是
// 但并非所有代码都已如此更新。当我们在“go vendor”中忽略构建标签时，我们会查看“/+build appengine”文件，而
// 可能会看到这些遗留导入。我们删除它们，以便模块
// 搜索不会寻找试图满足它们的模块。
func scanDir(dir string, tags map[string]bool) (imports_, testImports []string, err error) {
	imports_, testImports, err = imports.ScanDir(dir, tags)

	filter := func(x []string) []string {
		w := 0
		for _, pkg := range x {
			if pkg != "C" && pkg != "appengine" && !strings.HasPrefix(pkg, "appengine/") &&
				pkg != "appengine_internal" && !strings.HasPrefix(pkg, "appengine_internal/") {
				x[w] = pkg
				w++
			}
		}
		return x[:w]
	}

	return filter(imports_), filter(testImports), err
}

// buildStacks为每个包计算最小导入堆栈，
// 用于错误消息。完成后，
// 属于原始根集的包将具有pkg。stack==nil，
// 和其他包都有pkg。堆栈指向下一个
// 以最小链打包导入堆栈。
// 作为副作用，buildStacks还构建ld。包裹，
// 所有已装载包裹的列表。
func (ld *loader) buildStacks() {
	if len(ld.pkgs) > 0 {
		panic("buildStacks")
	}
	for _, pkg := range ld.roots {
		pkg.stack = pkg // 避免在下一个循环中处理的哨兵
		ld.pkgs = append(ld.pkgs, pkg)
	}
	for i := 0; i < len(ld.pkgs); i++ { // 非范围：追加到ld。pkgs in loop 
		pkg := ld.pkgs[i]
		for _, next := range pkg.imports {
			if next.stack == nil {
				next.stack = pkg
				ld.pkgs = append(ld.pkgs, next)
			}
		}
		if next := pkg.test; next != nil && next.stack == nil {
			next.stack = pkg
			ld.pkgs = append(ld.pkgs, next)
		}
	}
	for _, pkg := range ld.roots {
		pkg.stack = nil
	}
}

// stackText生成导入堆栈文本，以便在
// 报告pkg中的错误时使用。它的一般形式为
// 
// 根导入
// 其他导入
// 其他2由
// 其他2测试。test imports 
// pkg 
// 
func (pkg *loadPkg) stackText() string {
	var stack []*loadPkg
	for p := pkg; p != nil; p = p.stack {
		stack = append(stack, p)
	}

	var buf bytes.Buffer
	for i := len(stack) - 1; i >= 0; i-- {
		p := stack[i]
		fmt.Fprint(&buf, p.path)
		if p.testOf != nil {
			fmt.Fprint(&buf, ".test")
		}
		if i > 0 {
			if stack[i-1].testOf == p {
				fmt.Fprint(&buf, " tested by\n\t")
			} else {
				fmt.Fprint(&buf, " imports\n\t")
			}
		}
	}
	return buf.String()
}

// why返回要在关于给定包的“go mod why”输出中使用的文本。
// 它没有stackText华丽，但包含相同的信息。
func (pkg *loadPkg) why() string {
	var buf strings.Builder
	var stack []*loadPkg
	for p := pkg; p != nil; p = p.stack {
		stack = append(stack, p)
	}

	for i := len(stack) - 1; i >= 0; i-- {
		p := stack[i]
		if p.testOf != nil {
			fmt.Fprintf(&buf, "%s.test\n", p.testOf.path)
		} else {
			fmt.Fprintf(&buf, "%s\n", p.path)
		}
	}
	return buf.String()
}

// Why返回给定包的“go mod Why”输出节，
// 不带前导#注释。
// 包图必须已经加载，通常由LoadPackages加载。
// 如果包没有理由在当前版本中，
// 为什么返回空字符串。
func Why(path string) string {
	pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
	if !ok {
		return ""
	}
	return pkg.why()
}

// WhyDepth返回Why列表中的步骤数。
// 如果包没有理由在当前版本中，
// WhyDepth返回0。
func WhyDepth(path string) int {
	n := 0
	pkg, _ := loaded.pkgCache.Get(path).(*loadPkg)
	for p := pkg; p != nil; p = p.stack {
		n++
	}
	return n
}
